home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-04-12 | 9.7 KB | 232 lines | [TEXT/MPS ] |
- This document assumes that you have read or are familiar with the contents of the
- documents “AppWannabe.Tutorial” and “AppWannabe.multiple.doc.app”. This document
- addresses the needs of an application that has more than one document type.
-
- The sample application DTS.Draw demonstrates a good technique for handling
- multiple documents with the DTS application framework.
-
- The assumption here is that you have already implemented the first document type to
- some degree. If this is so and you have followed the method described by the
- document “AppWannabe.multiple.doc.app”, then you have already placed some code in the
- file “Window2.c”.
-
- The application framework was designed to make writing an application with a single
- document type. All that needs to be done for this is to add code to various empty
- or nearly empty functions within AppWannabe. Supporting more document types demands a
- different technique.
-
- You can consider the “Window2.c” file as the first document object. Almost all of the
- code for handling this document is contained in this single file. There is a little
- bit of setup code in the file “File2.c”, but for the most part the code for this initial
- document is in the file “Window2.c”.
-
- What you need for your second document is another “Window2.c” file. DTS.Draw demonstrates
- this. Here are two other document code files for DTS.Draw:
- Clipboard.c
- ToolPalette.c
-
- These files are quite similer in their content. They both have a document initialization
- function. In looking at this function, you can easily see how the document will differ
- from the primary document (whose code is contained in “Window2.c”).
-
- For the “Clipboard.c” file, here's the initialization function:
-
- OSErr ClipboardInitDocument(FileRecHndl frHndl)
- {
- OSErr err;
- FileRecPtr frPtr;
-
- err = DefaultInitDocument(frHndl, 0, 0, 0);
-
- if (!err) {
- frPtr = *frHndl;
- frPtr->fileState.windowID = rClipboardWindow;
- frPtr->fileState.calcFrameRgnProc = nil;
- frPtr->fileState.contentClickProc = ClipboardContentClick;
- frPtr->fileState.contentKeyProc = ClipboardContentKey;
- frPtr->fileState.drawFrameProc = nil;
- frPtr->fileState.freeDocumentProc = nil;
- frPtr->fileState.freeWindowProc = nil;
- frPtr->fileState.initContentProc = ClipboardInitContent;
- frPtr->fileState.readDocumentProc = nil;
- frPtr->fileState.readDocumentHeaderProc = nil;
- frPtr->fileState.resizeContentProc = nil;
- frPtr->fileState.scrollFrameProc = nil;
- frPtr->fileState.undoFixupProc = nil;
- frPtr->fileState.windowCursorProc = ClipboardWindowCursor;
- frPtr->fileState.writeDocumentProc = nil;
- frPtr->fileState.writeDocumentHeaderProc = nil;
-
- frPtr->fileState.fss.name[0] = 0; /* Use resource window name. */
- frPtr->fileState.attributes = kwClipboardWindow;
- }
-
- return(err);
- }
-
- Many of the document functions aren't needed for this document. The ones that aren't
- needed have the procedure pointer set to nil. If the procPtr is nil, then the
- application framework knows that the functionality for that function isn't needed for
- this document. For example: For the clipboard function, there is no CalcFrameRgn
- function, so the procPtr is simply set nil.
-
- Note that content clicks are handled differently than the primary document. Content
- clicks either scroll the clipboard or do nothing. The code for this is very simple:
-
- static void ClipboardContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
- {
- #pragma unused (frHndl, firstClick)
-
- IsCtlEvent(window, event, nil, nil);
- }
-
-
- IsCtlEvent handles document scrolling. Nothing else is needed. The ContentKey function is
- even simpler. Keypresses do nothing. All we need to do is to state that the window used
- the keypress is used. Here's the function:
-
- static Boolean ClipboardContentKey(WindowPtr window, EventRecord *event, Boolean *passThrough)
- {
- #pragma unused (window, event, passThrough)
-
- return(true);
- }
-
-
-
- This is how the behavior for the various documents is changed from the primary document.
- The procPtr is simply changed to point to a different function.
-
-
- Note that the ImageDocument procedure isn't changed or set to nil. That is because we
- want the ImageDocument function in the primary document to be called. Unless we change
- the procPtr, the primary document's function will be called. This is exactly what we
- want, as the clipboard is simply a non-editable standard document. All of the procPtrs
- that would support editing (or saving) the standard document have been changed.
-
- Note that we didn't change or nil out the functions for reading a document. They still
- point to the primary document's read code. This is fine because there will never be an
- instance where the clipboard can be opened and read. We don't have to worry about
- changing these procPtrs because it simply can't happen that they are called.
-
-
- The ClipboardInitDocument function is called from “File2.c”. Instead of having this
- initialization code within “File2.c” it is placed here and called. This allows a
- better grouping of the clipboard code. The more of it that is in one place, the
- easier it is to follow what the clipboard document does.
-
-
-
- Here's the initialization code for the tool palette document. You can see very quickly
- that it does practically nothing. It only handles clicks and drawing itself.
-
- OSErr ToolInitDocument(FileRecHndl frHndl)
- {
- FileRecPtr frPtr;
-
- frPtr = *frHndl;
- frPtr->fileState.windowID = rToolWindow;
- frPtr->fileState.getDocWindow = GetToolWindow;
- frPtr->fileState.calcFrameRgnProc = nil;
- frPtr->fileState.contentClickProc = ToolContentClick;
- frPtr->fileState.contentKeyProc = nil;
- frPtr->fileState.drawFrameProc = nil;
- frPtr->fileState.freeDocumentProc = nil;
- frPtr->fileState.freeWindowProc = nil;
- frPtr->fileState.imageProc = ToolImageDocument;
- frPtr->fileState.initContentProc = nil;
- frPtr->fileState.readDocumentProc = nil;
- frPtr->fileState.readDocumentHeaderProc = nil;
- frPtr->fileState.resizeContentProc = nil;
- frPtr->fileState.scrollFrameProc = nil;
- frPtr->fileState.undoFixupProc = nil;
- frPtr->fileState.windowCursorProc = nil;
- frPtr->fileState.writeDocumentProc = nil;
- frPtr->fileState.writeDocumentHeaderProc = nil;
-
- frPtr->fileState.fss.name[0] = 0; /* Use resource window name. */
- frPtr->fileState.attributes = kwToolWindow;
-
- return(noErr);
- }
-
-
- The GetToolWindow code places the initial window in the upper-right of the main screen.
- The constant kwToolWindow includes a bit that states that the window is a floating palette.
- That's all there is to creating this floating tool palette.
-
- There is some additional code for moving the window if the user tears off the tool menu.
- Again, it is logical to place this code here along with all of the other code for this
- document.
-
- Note that the concept of a “document” for this window is only a convenience. It can never
- be saved or opened or printed. Considering it a document allows us to use the framework
- to manage the floating palette.
-
- That's about all there is to using the framework for multiple documents. Currently all of
- the other multiple document issues will have to be handled by you. One such issue is managing
- menus. The framework has no support for this. You will have to find out the document type
- of the top window and adjust the menus accordingly.
-
- Another such issue is that some of these documents may be related. If you close a document,
- you may also need to close other documents that were created in relation to that document.
- Again, you will have to handle these issues yourself.
-
- There are interator functions in the framework that conveniently allow you to find the next
- document of a certain type. These iterators walk the window list looking for the next
- window that has a document of a certain type. This means that you can use these iterators
- only for documents that have a window.
-
-
-
- If you are a heavy user of the hierarchcal document architecture, you may end up with two
- different root object definitions for two different document types. It is convenient to
- store some non-undoable state data in the root object, but if two different documents have
- different state data, having only one root object type can get confusing. Due to this, you
- may find it useful to sub-type the root object in these instances.
-
- The standard root object definitions looks like this:
-
- long TRootObj(TreeObjHndl hndl, short message, long data);
- typedef struct {
- TreeObjHndl undo; /* This structure may be added to, but */
- FileRecHndl frHndl; /* these two first fields must remain. */
- } RootObj;
-
- You may wish to extend this to be something like the following:
-
- long TRootObj(TreeObjHndl hndl, short message, long data);
- typedef struct {
- TreeObjHndl undo; /* This structure may be added to, but */
- FileRecHndl frHndl; /* these two first fields must remain. */
- short subtype; /* Identifier for variant of root object. */
- union {
- Root1 r1; /* Union in the various root definitions here. */
- Root2 r2;
- } st;
- } RootObj;
-
-
- The code for the root object that is called by the framework would look something like:
-
- long TRootObj(TreeObjHndl hndl, short message, long data)
- {
- TreeObjProcPtr proc;
-
- if (proc = gRootMethods[mDerefRoot(hndl)->subtype])
- return((*proc)(hndl, message, data);
-
- return(0);
- }
-
- This code is dispatched to for a root object of whatever subtype. It in turn then dispatches
- to the code for the subtype of the root object. A table of procedures called gRootMethods is
- assumed here. It would be constructed similarly to the gTreeObjMethods table found in the
- file “File.c”.
-
-
- The rest is simply a matter of writing code. You will probably want to reference the various
- samples that use the framework to see how certain issues were addressed.
-
-
-